﻿/// SDMX Artefact Tiny Repository. A repository for SAE Editor.
///    Copyright (C) 2010  Philippe Prati 
///
///    This program is free software: you can redistribute it and/or modify
///    it under the terms of the GNU General Public License as published by
///    the Free Software Foundation, either version 2 of the License, or
///    (at your option) any later version.
///
///    This program is distributed in the hope that it will be useful,
///    but WITHOUT ANY WARRANTY; without even the implied warranty of
///    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
///    GNU General Public License for more details.
///
///    You should have received a copy of the GNU General Public License
///    along with this program.  If not, see <http://www.gnu.org/licenses/>.
///
///    Contact: ph.prati@bluewin.ch
///    
using System;
using System.Data;
using System.Web;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Services;
using System.Xml;

/// <summary>
/// Basic SDMX Structure Repository service
/// </summary>
[WebService(Namespace = "http://fmr/sms/sae.ch/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Repository : System.Web.Services.WebService
{
    #region C'tor

    public Repository () {

    }
    #endregion

    #region Request Helper

    /// <summary>
    /// Helper to check some entry parameters of methods
    /// </summary>
    /// <param name="user">user as Current WindowsIdentity</param>
    /// <param name="pwd">password for this service</param>
    /// <param name="artType">Type of artefact as string</param>
    /// <param name="fullId">ID + AgencyID + Version Nr</param>
    /// <returns>A Status type with Explanation message if status is not success</returns>
    private StatusMessage ProcessRequestStatus(string user, string pwd, string artType, string fullId)
    {
        return ProcessRequestStatus(user, pwd, artType, fullId, true);
    }

    private StatusMessage ProcessRequestStatus(string user, string pwd, string artType, string fullId, bool checkUser)
    {
        StatusMessage status = new StatusMessage(StatusType.Success, null);
        try
        {
            AuthorizeUser auth = new AuthorizeUser(user, pwd);
            if (checkUser && auth.IsAuthorized == false)
                status = new StatusMessage(StatusType.Failure, auth.RefuseMessage);
            else
            {
                ArtefactPrimaryKey key = new ArtefactPrimaryKey(artType, fullId);
                if (key.IsValid == false)
                    status = new StatusMessage(StatusType.Failure, "Invalid Artefact Key: " + fullId);
                if (key.IsValidType == false)
                    status = new StatusMessage(StatusType.Failure, "Invalid Artefact Type: " + artType);
            }
        }
        catch
        {
            status = new StatusMessage(StatusType.Failure, "Unable to process Artefact: " + fullId);
        }

        return status;
    }
    #endregion

    #region Test Method (Version)

    [WebMethod(Description = "Test allows to retrieve repository version")]
    public XmlElement Test()
    {
        string title = "TinyRegistry";
        StatusMessage status = new StatusMessage(StatusType.Success, title + " version: " +
            System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString());
        return status.Element;

    }
    #endregion

    #region Exist Artefact Method

    [WebMethod(Description = "Returns a Success status if an artefact of specified type holds the given fullId parameter")]
    public XmlElement ExistArtefact(string artType, string fullId)
    {
        StatusMessage status = ProcessRequestStatus(null, null, artType, fullId, false);
        if (status.Status == StatusType.Success)
        {
            bool exist = false;
            try
            {
                exist = Manager.Instance.UpdateList(new ArtefactPrimaryKey(artType, fullId),
                    ListOperation.Exist, null, null, null, false);
                if (exist == false)
                    status = new StatusMessage(StatusType.Warning, "Unknown Artefact: " + artType + ", " + fullId);
            }
            catch (Exception ex)
            {
                if (ex is ApplicationException)
                    status = new StatusMessage(StatusType.Failure, ex.Message);
                else
                    status = new StatusMessage(StatusType.Failure, "Unable to check existence of Artefact " + fullId);
            }
        }

        return status.Element;
    }
    #endregion

    #region Get Artefact Methods

    [WebMethod(Description = "Returns, in a SDMX Message document,  the artefact type instance matching the specified full Id." +
        " In case of error, an invalid status message is returned." +
        " In case of Ownership support, this method do not allow to retrieve NO diffusion status artefacts." +
        " NOTE: FullId = 'AgencyId+Id+Version'")]
    public XmlElement GetArtefact(string artType, string fullId)
    {
        XmlDocument xDoc = new XmlDocument();
        StatusMessage status = ProcessRequestStatus(null, null, artType, fullId, false);
        if (status.Status == StatusType.Success)
        {
            try
            {
                // In case of OwnerShipSupport, Only group authorized can get Artefact with diffusion status at NONE.
                Manager.Instance.UpdateList(new ArtefactPrimaryKey(artType, fullId),
                   ListOperation.Get, null, null, null, false);

                string path = Manager.Instance.RootPath + artType + "\\" + fullId + ".xml";
                xDoc.Load(path);
            }
            catch (Exception ex)
            {
                if (ex is ApplicationException)
                    status = new StatusMessage(StatusType.Failure, ex.Message);
                else
                    status = new StatusMessage(StatusType.Failure, "Unable to get Artefact " + fullId);
            }
        }

        return status.Status == StatusType.Success ? xDoc.DocumentElement : status.Element;
    }

    [WebMethod(Description = "Returns, in a SDMX Message document,  the artefact type instance matching the specified full Id." +
        " In case of error, an invalid status message is returned." +
        " In case of Ownership support, this method allows to retrieve NO diffusion artefact for the users of the Agency Group." +
        " NOTE: FullId = 'AgencyId+Id+Version'")]
    public XmlElement GetArtefactOS(string user, string pwd, string artType, string fullId)
    {
        XmlDocument xDoc = new XmlDocument();
        StatusMessage status = ProcessRequestStatus(user, pwd, artType, fullId);
        if (status.Status == StatusType.Success)
        {
            try
            {
                // In case of OwnerShipSupport, Only group authorized can get Artefact with diffusion status at NONE.
                Manager.Instance.UpdateList(new ArtefactPrimaryKey(artType, fullId),
                   ListOperation.Get, user, null, null, false);

                string path = Manager.Instance.RootPath + artType + "\\" + fullId + ".xml";
                xDoc.Load(path);
            }
            catch (Exception ex)
            {
                if (ex is ApplicationException)
                    status = new StatusMessage(StatusType.Failure, ex.Message);
                else
                    status = new StatusMessage(StatusType.Failure, "Unable to get Artefact " + fullId);
            }
        }

        return status.Status == StatusType.Success ? xDoc.DocumentElement : status.Element;
    }
    #endregion

    #region get Artefact list, GetInfo Method

    [WebMethod(Description = "Returns an Information list of all artefacts of specified type. The list is an XML document with embedded scheme.")]
    public DataSet DirInfo(string artType)
    {
        return Manager.Instance.GetList(artType);
    }

    [WebMethod(Description = "Returns an Information list of all artefacts satisfying the following conditions: " +
                          " (1) the artefact type matches the specified artefact pattern, " +
                          " (2) the agency id matches the specified agency pattern, " +
                          " (3) the artefact id matches the specified identifier pattern, " +
                          " (4) the artefact version matches the specified version, and " +
                          " (5) the artefact has been modified or created in the period delimited by the specified starting and ending date.")]
    public DataSet GetArtefactInfos(string artTypePattern, string agencyPattern, string idPattern, string versionPattern, DateTime start, DateTime end)
    {
        Regex artTypeRegex = new Regex(artTypePattern);
        Regex agencyRegex = new Regex(agencyPattern);
        Regex idRegex = new Regex(idPattern);
        Regex versionRegex = new Regex(versionPattern);

        DataSet result = new DataSet();
        foreach (string artType in Manager.Instance.GetArtefactTypes())
        {
            if (artTypeRegex.IsMatch(artType))
            {
                DataTable table = Manager.Instance.GetList(artType).Tables[0];
                DataTable resultTable = table.Clone();
                foreach (DataRow row in table.Rows)
                {
                    string agency = row["AI"].ToString();
                    string id = row["ID"].ToString();
                    string version = row["VF"].ToString();
                    DateTime lastUpdate = DateTime.Parse(row["LU"].ToString());

                    if (agencyRegex.IsMatch(agency) && idRegex.IsMatch(id) && versionRegex.IsMatch(version) &&
                        (start <= lastUpdate) && (end > lastUpdate))
                    {
                        resultTable.Rows.Add(row.ItemArray);
                    }
                }
                result.Tables.Add(resultTable);
            }
        }
        return result;
    }
    #endregion

    #region Submit Artefact Methods

    [WebMethod(Description = "Submits a new artefact to registry or updates existing one (if checked out by same user)" +
        " NOTE: FullId = 'AgencyId+Id+Version'")]
    public XmlElement SubmitArtefact(string user, string pwd, string artType, string fullId, XmlElement elArtefact)
    {
        return SubmitArtefactCommon(user, pwd, artType, fullId, elArtefact, false);
    }

    [WebMethod(Description = "NEW : Submits and backups a new artefact to registry or updates existing one (if checked out by same user)" +
    " NOTE: FullId = 'AgencyId+Id+Version'")]
    public XmlElement SubmitArtefactBackup(string user, string pwd, string artType, string fullId, XmlElement elArtefact)
    {
        return SubmitArtefactCommon(user, pwd, artType, fullId, elArtefact, true);
    }


    private XmlElement SubmitArtefactCommon(string user, string pwd, string artType, string fullId, XmlElement elArtefact, bool forceBackup)
    {
        StatusMessage status = ProcessRequestStatus(user, pwd, artType, fullId);

        try
        {
            if (status.Status == StatusType.Success)
            {
                // Get artefact document 
                XmlDocument xDoc = new XmlDocument();
                xDoc.LoadXml(elArtefact.OuterXml);

                // Locate Artefact in document  from it's id and extract names
                ArtefactLocator locator = new ArtefactLocator(xDoc);
                ArtefactPrimaryKey key = new ArtefactPrimaryKey(artType, fullId);
                if (locator.LocateArtefact(key) == false)
                    key = new ArtefactPrimaryKey(artType, fullId.ToUpper());
                if (locator.LocateArtefact(key))
                {
                    // Save submitted artefact document and update the information list
                    StringBuilder sbPath = new StringBuilder(Manager.Instance.RootPath);
                    sbPath.Append(key.ArtefactType).Append(System.IO.Path.DirectorySeparatorChar).Append(key.FullIdent).Append(".xml");
                    Manager.Instance.UpdateList(key, ListOperation.Update, user, locator.Tts, 
                        null, locator.Aggregated, sbPath.ToString(), xDoc, forceBackup);

                    // If OwnershipSupport update if needed
                    if (Manager.OwnerShip.OrganisationSchemeFullId == fullId)
                        Manager.OwnerShip.UpdateGroupOwnerList(xDoc);
                }
                else
                    status = new StatusMessage(StatusType.Failure, "Unable to locate Artefact " + fullId + " in submitted XML Document.");

            }
        }
        catch (Exception ex)
        {
            if (ex is ApplicationException)
                status = new StatusMessage(StatusType.Failure, ex.Message);
            else
                status = new StatusMessage(StatusType.Failure, "Unable to register Artefact " + fullId);
        }

        return status.Element;
    }
    #endregion

    #region Reserve Artefact, Checkout Method
    [WebMethod(Description = "Reserves specified artefact for user with indicated Windows Identity.")]
    public XmlElement CheckOut(string user, string pwd, string artType, string fullId)
    {
        StatusMessage status = ProcessRequestStatus(user, pwd, artType, fullId);
        try
        {
            if (status.Status == StatusType.Success)
                Manager.Instance.UpdateList(new ArtefactPrimaryKey(artType, fullId), ListOperation.CheckOut,
                    user, null, null, false);
        }
        catch (Exception ex)
        {
            if (ex is ApplicationException)
                status = new StatusMessage(StatusType.Failure, ex.Message);
            else
                status = new StatusMessage(StatusType.Failure, "Unable to process Artefact " + fullId);
        }

        return status.Element;
    }
    #endregion

    #region Cancel reservation, Method UndoCheckout

    [WebMethod(Description = "Cancel Reservation by user on specified artefact.")]
    public XmlElement UndoCheckOut(string user, string pwd, string artType, string fullId)
    {
        StatusMessage status = ProcessRequestStatus(user, pwd, artType, fullId);
        try
        {
            if (status.Status == StatusType.Success)
                Manager.Instance.UpdateList(new ArtefactPrimaryKey(artType, fullId), ListOperation.CheckIn,
                    user, null, null, false);
        }
        catch (Exception ex)
        {
            if (ex is ApplicationException)
                status = new StatusMessage(StatusType.Failure, ex.Message);
            else
                status = new StatusMessage(StatusType.Failure, "Unable to process Artefact " + fullId);
        }

        return status.Element;
    }
    #endregion

    #region Define Dissemination status Method

    [WebMethod(Description = "Defines Dissemination status for artefact.(0-NO, 1-Public, 2-Intern)")]
    public XmlElement SetDisseminationStatus(string user, string pwd, string artType, string fullId, string diffusionCd)
    {
        StatusMessage status = ProcessRequestStatus(user, pwd, artType, fullId);
        try
        {
            if (status.Status == StatusType.Success)
                Manager.Instance.UpdateList(new ArtefactPrimaryKey(artType, fullId), ListOperation.SetDiffStatus,
                    user, null, diffusionCd, false);
        }
        catch (Exception ex)
        {
            if (ex is ApplicationException)
                status = new StatusMessage(StatusType.Failure, ex.Message);
            else
                status = new StatusMessage(StatusType.Failure, "Unable to process Artefact " + fullId);
        }

        return status.Element;
    }
    #endregion

    #region Delete Artefact Method

    [WebMethod(Description = "Deletes selected artefact, event if currently checked-out.")]
    public XmlElement DeleteArtefact(string user, string pwd, string artType, string fullId)
    {
        StatusMessage status = ProcessRequestStatus(user, pwd, artType, fullId);
        try
        {
            if (status.Status == StatusType.Success)
            {
                ArtefactPrimaryKey key = new ArtefactPrimaryKey(artType, fullId);
                string path = Manager.Instance.RootPath + key.ArtefactType + "\\" + key.FullIdent + ".xml";

                Manager.Instance.UpdateList(new ArtefactPrimaryKey(artType, fullId), ListOperation.Delete,
                    user, null, null, false, path, null, false);

                // Now remove it as file
                System.IO.File.Delete(path);
            }

        }
        catch (Exception ex)
        {
            if (ex is ApplicationException)
                status = new StatusMessage(StatusType.Failure, ex.Message);
            else
                status = new StatusMessage(StatusType.Failure, "Unable to process Artefact " + fullId);
        }

        return status.Element;
    }
    #endregion

    #region Get Backup List Method

    [WebMethod(Description = "NEW : Retieve the list of all backups for an Artefact.")]
    public DataSet GetArtefactBackupList(string arType, string fullId)
    {
        return Manager.Instance.GetArtefactBackupList(arType, fullId);
    }
    #endregion

    #region Get Backuped artefact Method

    // DEC update 17.08.2011
    [WebMethod(Description = "NEW : Returns, in a SDMX Message document, the versionned artefact type instance matching the specified full Id." +
    " In case of error, an invalid status message is returned." +
    " In case of Ownership support, this method allows to retrieve NO diffusion artefact for the users of the Agency Group." +
    " NOTE: FullId = 'AgencyId+Id+Version', indicates versionNumber as backup number")]
    public XmlElement GetArtefactBackup(string user, string pwd, string artType, string fullId, string versionNumber)
    {
        XmlDocument xDoc = new XmlDocument();
        StatusMessage status = ProcessRequestStatus(user, pwd, artType, fullId);
        if (status.Status == StatusType.Success)
        {
            try
            {
                // In case of OwnerShipSupport, Only group authorized can get Artefact with diffusion status at NONE.
                Manager.Instance.UpdateList(new ArtefactPrimaryKey(artType, fullId),
                   ListOperation.Get, user, null, null, false);

                BackupManager bakMgr = new BackupManager(artType, fullId);
                string pathBackup = bakMgr.CreateBackupPath(Convert.ToInt32(versionNumber));

                xDoc.Load(pathBackup);
            }
            catch (Exception ex)
            {
                if (ex is ApplicationException)
                    status = new StatusMessage(StatusType.Failure, ex.Message);
                else
                    status = new StatusMessage(StatusType.Failure, "Unable to get Artefact " + fullId + " in the requested version (" + versionNumber + ")");
            }
        }

        return status.Status == StatusType.Success ? xDoc.DocumentElement : status.Element;
    }
    #endregion

    #region Automatic backup information method

    [WebMethod(Description = "NEW : Indicates option value for Backup automatic on WS side")]
    public bool BackupAutomaticEnable()
    {
        return (BackupManager.BackupAutomatic);
    }
    #endregion

    #region Is user an Administrator Method

    [WebMethod(Description = "Returns a Success status if the user is an administrator of the Repository")]
    public XmlElement IsAdministrator(string user, string pwd)
    {
        StatusMessage status = new StatusMessage(StatusType.Failure, null);
        AuthorizeUser auth = new AuthorizeUser(user, pwd);
        if (auth.IsAuthorized == false)
            status = new StatusMessage(StatusType.Failure, auth.RefuseMessage);
        else
        {
            try
            {
                // Signal any user as administrator if ownership mechanism not used, else check real admin
                bool admin = Manager.OwnerShip.IsUsed == false || Manager.OwnerShip.IsSystemAdmin(ref user);
                status = new StatusMessage(StatusType.Success, admin.ToString());
            }
            catch (Exception ex)
            {
                status = new StatusMessage(StatusType.Failure, ex.Message);
            }
        }

        return status.Element;
    }
    #endregion

    #region Is Owner Method

    [WebMethod(Description = "Returns a Success status if the user is owner of the proposed artefact")]
    public XmlElement IsOwner(string winUser, string pwd, string fullId)
    {
        StatusMessage status = new StatusMessage(StatusType.Failure, null);
        string user = winUser.ToLower();
        AuthorizeUser auth = new AuthorizeUser(user, pwd);
        if (auth.IsAuthorized == false)
            status = new StatusMessage(StatusType.Failure, auth.RefuseMessage);
        else
        {
            try
            {
                 Manager.Instance.IsOwner(new ArtefactPrimaryKey("Codelists", fullId), user);
                status = new StatusMessage(StatusType.Success, "is Owner");
            }
            catch (Exception ex)
            {
                status = new StatusMessage(StatusType.Failure, ex.Message);
            }
        }

        return status.Element;
    }
    #endregion


    #region UpdateVersions

    [WebMethod(Description = "Returns, in a SDMX Message document as codelist, the versions updated form the specified artefact " +
    " NOTE: FullId = 'AgencyId+Id+Version'")]
    public XmlElement GetVersionEvolution(string artType, string fullId)
    {
        StatusMessage status = new StatusMessage(StatusType.Success, null);
        XmlDocument xDoc = new XmlDocument();
        try
        {
            ArtefactPrimaryKey key = new ArtefactPrimaryKey(artType, fullId);
            if (key.IsValid == false)
                status = new StatusMessage(StatusType.Failure, "Invalid Artefact Key: " + fullId);
            if (key.IsValidType == false)
                status = new StatusMessage(StatusType.Failure, "Invalid Artefact Type: " + artType);

            xDoc = Manager.Instance.GetUpdateInfo(key);
        }
        catch (Exception ex)
        {
            if (ex is ApplicationException)
                status = new StatusMessage(StatusType.Failure, ex.Message);
            else
                status = new StatusMessage(StatusType.Failure, "Unable to get Artefact " + fullId);
        }

        return status.Status == StatusType.Success ? xDoc.DocumentElement : status.Element;
    }
    #endregion
}

